package codebot.processor;

import codebot.processor.languages.Lang;
import codebot.processor.logic.Score;

public final class TextProcessor {
  // string constants
  private final static String TAG_CODE_OPEN = "<c>";
  private final static String TAG_CODE_CLOSE = "</c>";
  private final static String SEP_CODE = " \n\n";
  private final static String INDENTATION_UNIT = "  ";// tab = 2 spaces

  // functional parameters
  private final static String COLOR_CODE = "rgb(0,100,0)";
  private final static String COLOR_STRING = "rgb(3,17,255)";
  private final static String COLOR_ANNOTATION = "rgb(128,128,128)";

  /**
   * Function that changes the code between <c> and </c>
   * 
   * @param tv
   *          The TextView to be changed
   */
  public final static void process(IScreen screen) {
    String text = screen.getText();
    boolean live = false;
    int end = text.lastIndexOf(TAG_CODE_CLOSE);
    if (end == -1) {
      live = true;
      end = text.lastIndexOf(TAG_CODE_OPEN);
      if (end == -1) {
        return;
      }
    }

    // screen.log("last tag's location: " + end);

    final int start = text.indexOf(TAG_CODE_OPEN);
    if (start == -1 || start == end) {
      return;
    }

    // screen.log("tag locations established: " + start + " " + end);

    // adding new lines at the beginning and end, instead of the tags
    if (!live) {
      screen.replace(start, start + TAG_CODE_OPEN.length() - 1, SEP_CODE);
      screen.replace(end, end + TAG_CODE_CLOSE.length() - 1, SEP_CODE);
    }
    // screen.log("added new lines at the beginning and end, instead of the tags");

    // indentation
    StringBuilder sb = new StringBuilder(screen.getText(start, end));
    if (!live) {
      /*
       * code = code.replace("\n", "\n  "); screen.replace(start, end, code);
       */
      int i = -1;
      while ((i = sb.indexOf("\n", i + 1)) > 0) {
        sb.insert(i + 1, INDENTATION_UNIT);
        screen.insert(start + i + 1, INDENTATION_UNIT);
      }
    }

    String code = sb.toString();
    int s = start + SEP_CODE.length();
    int e = start + SEP_CODE.length() + code.length()
        - (live ? TAG_CODE_OPEN.length() : 0) - 1;

    // setting color of code
    screen.setColor(COLOR_CODE, s, e);

    // setting bold weight
    screen.setBold(true, s, e);

    // setting monospace font
    screen.setFont("Courier New", s, e);

    // looking for any comments and marking them out
    TextProcessor.processGeneralSyntax(screen, code, s, e);
  }

  /**
   * Processes the content of the code. Is also needed to find out which is the
   * most likely language.
   * 
   * @param tv
   *          The TextView which contains the code
   * @param code
   *          The code as a string
   * @param s
   * @param e
   */
  public final static void processGeneralSyntax(IScreen screen, String code,
      int s, int e) {
    final Score score = new Score();
    final int codeStart = s - 3; // TODO correct this -3 correction
    // :) Wave's fault
    int state = 0, i = 0, start = -1, end = -1;
    boolean inMethod = false; // sees if you're in a method
    while (i < e-s) {
      switch (state) {
      case 0:
        switch (code.charAt(i)) {
        case '/':
          state = 1;
          break;
        case '"':
          state = 5;
          start = i; // memorizing the beginning of the string
          break;
        case '\'':
          state = 6;
          start = i; // memorizing the beginning of the string
          break;
        case '<':
          state = 7;
          score.increment(Score.sXml);
          break;
        case '@':
          if (!inMethod) {
            state = 13;
            start = i; // memorizing the beginning of the Java annotation
          }
          break;
        case ')': // TODO not in state diagram
          state = 14;
          break;
        case '}':
          inMethod = false;
          break;
        }
        break;
      case 1:
        switch (code.charAt(i)) {
        case '/':
          state = 2; // we have a comment? '//'
          start = i - 1; // memorizing the beginning of the comment
          break;
        case '*':
          state = 3; // we have a comment? '/*'
          start = i - 1; // memorizing the beginning of the comment
          break;
        default:
          state = 0;
        }
        break;
      case 2:
        switch (code.charAt(i)) {
        case '\n':
          state = 0; // comment is over
          end = i; // memorizing the end of the comment
          screen.setBold(false, codeStart + start, codeStart + end);
          score.increment(Score.sCFamily);
          break;
        }
        break;
      case 3:
        switch (code.charAt(i)) {
        case '*':
          state = 4; // this might be part of '*/' but we have to make sure in
          // state 4
          break;
        }
        break;
      case 4:
        switch (code.charAt(i)) {
        case '/':
          state = 0; // comment is over
          end = i + 1; // memorizing the end of the comment
          screen.setBold(false, codeStart + start, codeStart + end);
          score.increment(Score.sCFamily);
          break;
        }
        break;
      case 5:
        switch (code.charAt(i)) {
        case '"':
          state = 0;
          end = i + 1; // string is over
          screen.setItalic(true, codeStart + start, codeStart + end);
          screen.setColor(COLOR_STRING, codeStart + start, codeStart + end - 1);
          break;
        }
        break;
      case 6:
        switch (code.charAt(i)) {
        case '\'':
          state = 0;
          end = i + 1;
          screen.setItalic(true, codeStart + start, codeStart + end);
          screen.setColor(COLOR_STRING, codeStart + start, codeStart + end);
          break;
        }
        break;
      case 7:
        switch (code.charAt(i)) {
        case '!':
          state = 8; // we might have an XML comment: '<!-- bla -->'
          break;
        default:
          state = 0;
        }
        break;
      case 8:
        switch (code.charAt(i)) {
        case '-':
          state = 9;
          break;
        default:
          state = 0;
        }
        break;
      case 9:
        switch (code.charAt(i)) {
        case '-':
          state = 10;
          start = i - 3; // the start of an XML comment: '<!-- bla -->'
          break;
        default:
          state = 0;
        }
        break;
      case 10:
        switch (code.charAt(i)) {
        case '-':
          state = 11; // the XML comment might end
          break;
        }
        break;
      case 11:
        switch (code.charAt(i)) {
        case '-':
          state = 12; // the XML comment might end
          break;
        }
        break;
      case 12:
        switch (code.charAt(i)) {
        case '>':
          state = 0; // the XML comment has ended
          end = i + 1;
          screen.setBold(false, codeStart + start, codeStart + end);
          break;
        }
        break;
      case 13:
        switch (code.charAt(i)) {
        case '\n':
          state = 0; // the Java annotation has ended
          end = i;
          screen.setColor(COLOR_ANNOTATION, codeStart + start, codeStart + end);
          score.increment(Score.sJava);
          break;
        }
        break;
      case 14:
        switch (code.charAt(i)) {
        case ' ':
        case '\n':
          break;
        case '{': // a method implementation begins
          inMethod = true;
          score.increment(Score.sCFamily);
          state = 0;
          break;
        }
        break;
      }
      i++;
    }
    Lang l = Lang.getSpecificSyntaxProcessor(score);
    l.processSpecificSyntax(screen, code, s, e);
  }
}
